// 物模型
package tsl

import (
	"encoding/json"
	"fmt"
	"regexp"
	"strings"
	"github.com/tidwall/gjson"
)

//解析方法
func parseFunctions(d string, key string) ([]Function, error) {
	list := []Function{}
	functions := gjson.Get(d, key)
	if !functions.Exists() || functions.Value() == nil {
		return list, nil
	}
	var err1 error
	functions.ForEach(func(key, value gjson.Result) bool {
		p := Function{}
		inputs, err := parsePropertys(value.Raw, "inputs")
		if err != nil {
			err1 = err
			return false
		}
		p.Inputs = inputs
		outputval := value.Get("output")
		if len(outputval.Map()) > 0 {
			output, err := parseProperty(outputval, false)
			output.setId("result")
			if err != nil {
				err1 = err
				return false
			}
			p.Output = output
		}
		p.Id = value.Get("id").String()
		err = idCheck(p.Id)
		if err != nil {
			err1 = err
			return false
		}
		p.Name = value.Get("name").String()
		p.Async = value.Get("async").Bool()
		p.Expands = map[string]string{}
		p.Description = value.Get("description").String()
		expands := value.Get("expands")
		if len(expands.Map()) > 0 {
			err = json.Unmarshal([]byte(value.Get("expands").Raw), &p.Expands)
			if err != nil {
				err1 = fmt.Errorf("function has error: [%s], data: %s", err.Error(), string(d))
				return false
			}
		}
		list = append(list, p)
		return true
	})
	{
		var idMap map[string]bool = map[string]bool{}
		for _, v := range list {
			if _, ok := idMap[v.Id]; ok {
				return nil, fmt.Errorf("function is repeat [%s]", v.Id)
			} else {
				idMap[v.Id] = true
			}
		}
	}
	if err1 != nil {
		return nil, err1
	}
	return list, nil
}

//解析属性
func parsePropertys(d string, key string) ([]Property, error) {
	var list []Property
	res := gjson.Get(d, key)
	if !res.Exists() || res.Value() == nil {
		return list, nil
	}
	var err error
	res.ForEach(func(key, value gjson.Result) bool {
		var property Property
		property, err = parseProperty(value, true)
		if err != nil {
			return false
		}
		list = append(list, property)
		return true
	})
	{
		var idMap map[string]bool = map[string]bool{}
		for _, v := range list {
			if obj, ok := v.IsObject(); ok {
				if len(obj.Properties) == 0 {
					return list, fmt.Errorf("%s [%s] must have properties", key, v.GetId())
				}
			}
			if _, ok := idMap[v.GetId()]; ok {
				return list, fmt.Errorf("%s is repeat [%s]", key, v.GetId())
			} else {
				idMap[v.GetId()] = true
			}
		}
	}

	return list, err
}

//解析属性
func parseProperty(value gjson.Result, idMustNotNull bool) (Property, error) {
	var err error
	ptype := gjson.Get(value.Raw, "type")
	var property Property
	typeName := ptype.String()
	if len(typeName) == 0 {
		err = fmt.Errorf("type is not exist: %s", value.Raw)
		return nil, err
	}
	switch typeName {
		case TypeEnum:
			property = &PropertyEnum{}
		case TypeInt:
			property = &PropertyInt{}
		case TypeLong:
			property = &PropertyLong{}
		case TypeString:
			property = &PropertyString{}
		case TypeBool:
			property = &PropertyBool{}
		case TypePassword:
			property = &PropertyPassword{}
		case TypeFloat:
			property = &PropertyFloat{}
		case TypeDouble:
			property = &PropertyDouble{}
		case TypeDate:
			property = &PropertyDate{}
		case TypeFile:
			property = &PropertyFile{}
		case TypeObject:
			property = &PropertyObject{}
		default:
			err = fmt.Errorf("type %v is not support", typeName)
			return nil, err
	}
	property.setType(property.GetType())
	switch d1 := property.(type) {
	case *PropertyObject:
		d1.Id = value.Get("id").String()
		d1.Name = value.Get("name").String()
		d1.Expands = map[string]string{}
		d1.Description = value.Get("description").String()
		value.Get("expands").ForEach(func(key, value gjson.Result) bool {
			d1.Expands[key.String()] = value.String()
			return true
		})
		var properties []Property
		value.Get("properties").ForEach(func(key, value gjson.Result) bool {
			p, e := parseProperty(value, idMustNotNull)
			if e != nil {
				err = e
				return false
			}
			properties = append(properties, p)
			return true
		})
		d1.Properties = properties
	default:
		err = json.Unmarshal([]byte(value.Raw), property)
	}
	if err != nil {
		return nil, err
	}
	if idMustNotNull {
		if len(strings.TrimSpace(property.GetId())) == 0 {
			err = fmt.Errorf("id of BaseProperty must be persent")
			return nil, err
		}
		err = idCheck(property.GetId())
		if err != nil {
			return nil, err
		}
	}
	return property, err
}

func idCheck(id string) error {
	matched, _ := regexp.Match("^[0-9a-zA-Z_\\-]+$", []byte(id))
	if !matched {
		return fmt.Errorf("id [%s] is invalid, must be alphabet,number,underscores", id)
	}
	return nil
}